/*
    nspire-gamekit - a simple homebrew game framework
    Copyright (C) 2011  Daniel Tang

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <os.h>
#include "random.h"

static char init = 0;
static int mt[624];
static int index = 0;

static void gameRandomInit();
static void gameGenerateNumbers();
static unsigned int gameRandomMurmurHash2( const void * key, unsigned int seed );

//Mersenne twister - hopefully got it right.
//A sample of 13000 numbers on a scatter graph tells me I got it right
//Btw, this is only designed to work with 32bit data types.
//If sizeof(int) != 4, we have a problem, so yeah, unportable as heck
int gameRandomInteger() {
    if (!init) gameRandomInit();
    int y;
    if (index == 0) {
        gameGenerateNumbers();
    }
    
    y  = mt[index];
    y ^= y>>11;
    y ^= (y<<7) & 0x9d2c5680;
    y ^= (y<<15) & 0xefc60000;
    y ^= y>>18;
    
    index = (index + 1) % 624;
    
    return y;
}

static unsigned int gameRandomMurmurHash2( const void * key, unsigned int seed )
{
    //Modified from http://sites.google.com/site/murmurhash/
    //Only here for avalanche effect
    //Seed and RTC clock put together
    
	const unsigned int m = 0x5bd1e995;
	const int r = 24;
	unsigned int h = seed ^ 4;
	const unsigned char * data = (const unsigned char *)key;
	unsigned int k = *(unsigned int *)data;
	k *= m; 
	k ^= k >> r; 
	k *= m; 
	h *= m; 
	h ^= k;
	h ^= h >> 13;
	h *= m;
	h ^= h >> 15;
	return h;
}

static void gameRandomInit() {
    int seed, i;
    
    //Trying to get a random seed
    void* heap = malloc(sizeof(int));
    memcpy(&seed, heap, sizeof(int));
    free(heap);
    seed = gameRandomMurmurHash2((void*)0x90090000, seed);
    //Hash the RTC time with random heap data
    
    mt[0] = seed;
    for (i=1;i<624;i++) {
        mt[i] = 0x6C078965UL * ((mt[i-1] ^ ( mt[i-1]>>30 )) + i);
    }
    init = 1;
}

static void gameGenerateNumbers() {
    int y, i;
    for (i=0;i<624;i++) {
        y = (mt[i] & 0x80000000) + ((mt[i+1] % 624) & 0x7FFFFFFF);
        mt[i] = mt[(i+397) % 624] ^ (y>>1);
        if ((y % 2) != 0) {
            mt[i] ^= 0x9908b0df;
        }
    }
}